#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>

#define SIZE 512
typedef void (*FuncType)();
char *myArgv[SIZE];

const char *GetUsername()
{
    const char *username = getenv("USER");
    if (username == NULL)
        return "NULL";
    return username;
}

const char *GetHostname()
{
    const char *hostname = getenv("HOSTNAME");
    if (hostname == NULL)
        return "NULL";
    return hostname;
}

const char *GetCWD()
{
    const char *pwd = getenv("PWD");
    if (pwd == NULL)
        return "NULL";
    const char *cwd = pwd + strlen(pwd) - 1;
    while (*cwd != '/')
        --cwd;
    if (cwd != pwd)
        cwd += 1;
    return cwd;
}

const char *GetPWD()
{
    const char *pwd = getenv("PWD");
    if (pwd == NULL)
        return "NULL";
    return pwd;
}

const char *GetHome()
{
    const char *home = getenv("HOME");
    if (home == NULL)
        return "NULL";
    return home;
}

void MakeCommandLine()
{
    char line[SIZE];
    const char *username = GetUsername();
    const char *hostname = GetHostname();
    const char *cwd = GetCWD();
    snprintf(line, sizeof(line), "[%s@%s: %s]> ", username, hostname, cwd);
    printf("%s", line);
    fflush(stdout);
}

int GetUserCommand(char command[], size_t n)
{
    char *s = fgets(command, n, stdin);
    if (s == NULL)
        return -1;
    command[strlen(command) - 1] = '\0';
    // printf("%s\n", command);
    return strlen(command);
}

void SplitCommand(char command[], size_t n)
{
    myArgv[0] = strtok(command, " ");
    int index = 1;
    while (myArgv[index++] = strtok(NULL, " "))
        ;
}

void CD()
{
    const char *path = myArgv[0];
    if (path == NULL)
        path = GetHome();

    chdir(path);
    printf("%s\n", path);
    const char *pwd = GetPWD();
    char tmp[2 * SIZE];
    snprintf(tmp, sizeof(tmp), "PWD=%s", pwd);
    putenv(tmp);
}

int CheckBuildin()
{
    char *buildin[] =
        {
            "cd",
            NULL};

    FuncType pf[] =
        {
            CD,
            NULL};

    for (int i = 0; buildin[i]; ++i)
    {
        if (strcmp(myArgv[0], buildin[i]) == 0)
        {
            pf[i]();
            return 1;
        }
    }
    return 0;
}

void ExecuteCommand()
{
    pid_t id = fork();
    if (id == 0)
    {
        execvp(myArgv[0], myArgv);
        exit(errno);
    }
    else
    {
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if (rid > 0)
        {
            int errcode = WEXITSTATUS(status);
            if (errcode > 0)
                printf("%s: %s\n", myArgv[0], strerror(errcode));
        }
    }
}

int main()
{
    bool quit = false;
    while (!quit)
    {
        // 1. 输出自己的命令行
        MakeCommandLine();
        // 2. 获取命令
        char command[SIZE];
        int n = GetUserCommand(command, sizeof(command));
        if (n < 0)
            continue;
        // 3. 命令分割
        SplitCommand(command, sizeof(command));
        // 4. 判断并执行内建命令
        n = CheckBuildin();
        if (n)
            continue;
        // 5. 执行命令
        ExecuteCommand();
    }
    return 0;
}